package com.cmc.cloud.cmclink.doc.service.impl.si;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.*;
import com.cmc.cloud.cmclink.doc.convert.VoyageLockConvert;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.service.si.BlnoLocalCostExService;
import com.cmc.cloud.cmclink.doc.service.si.VoyageLockService;
import com.cmc.cloud.cmclink.doc.util.I18nsUtil;
import com.cmc.cloud.cmclink.doc.util.VslvoyUtil;
import com.cmc.cloud.cmclink.doc.vo.blnochargevo.BlnoCheckMsgReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnospiltcombinevo.BlnoSpiltCombinePageReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoPageReqVO;
import com.cmc.cloud.cmclink.doc.vo.blnovo.BlnoPageRespVO;
import com.cmc.cloud.cmclink.doc.vo.confirmnoticevo.ConfirmNoticePageReqVO;
import com.cmc.cloud.cmclink.doc.vo.confirmnoticevo.ConfirmNoticeRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoPageReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoRespVO;
import com.cmc.cloud.cmclink.doc.vo.voyagelock.*;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.ex.Assertion;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;

import static com.cmc.cloud.cmclink.doc.api.document.emums.OperateTypeEnum.EXPORT_LOCK_FREIGHT;
import static com.cmc.cloud.cmclink.doc.api.document.emums.OperateTypeEnum.IMPORT_LOCK_FREIGHT;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SiOperateTypeEnum.OPERATE_SI_SEVENTEEN;
import static com.cmc.cloud.cmclink.doc.api.document.emums.SoStatusEnum.BookingConfirmed;
import static com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoSpiltCombineErrorCodeConstants.SO_AMEND_APPLYING_EXISTS;


/**
 * 航次锁船记录 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
public class VoyageLockServiceImpl implements VoyageLockService {

    @Resource
    private VoyageLockMapper voyageLockMapper;
    @Resource
    private BlnoMapper blnoMapper;
    @Resource
    private SoMapper soMapper;
    @Resource
    private BlnoVslvoyMapper blnoVslvoyMapper;
    @Resource
    private BlnoFreightMapper blnoFreightMapper;
    @Resource
    private BlnoLocalCostMapper blnoLocalCostMapper;
    @Resource
    private BlnoLocalChargeMapper blnoLocalChargeMapper;

    @Resource
    private OperateLogMapper operateLogMapper;

    @Resource
    private BlnoLocalCostExService blnoLocalCostExService;
    @Resource
    private I18nsUtil i18nsUtil;

    @Resource
    private VslvoyUtil vslvoyUtil;

    @Resource
    private BlnoSpiltCombineMapper blnoSpiltCombineMapper;

    @Resource
    private ConfirmNoticeMapper confirmNoticeMapper;

    @Override
    public void createVoyageLock(VoyageLockCreateReqVO createReqVO) {
        // 插入
        VoyageLockDO voyageLock = VoyageLockConvert.INSTANCE.convert(createReqVO);
        voyageLockMapper.insert(voyageLock);
    }

    @Override
    public void updateVoyageLock(VoyageLockUpdateReqVO updateReqVO) {
        // 校验存在
        validateVoyageLockExists(updateReqVO.getId());
        // 更新
        VoyageLockDO updateObj = VoyageLockConvert.INSTANCE.convert(updateReqVO);
        voyageLockMapper.updateById(updateObj);
    }

    @Override
    public void deleteVoyageLock(Long id) {
        // 校验存在
        validateVoyageLockExists(id);
        // 删除
        voyageLockMapper.deleteById(id);
    }

    private void validateVoyageLockExists(Long id) {
        if (voyageLockMapper.selectById(id) == null) {
            throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.VOYAGE_LOCK_NOT_EXISTS);
        }
    }

    @Override
    public VoyageLockDO getVoyageLock(Long id) {
        return voyageLockMapper.selectById(id);
    }

    @Override
    public List<VoyageLockDO> getVoyageLockList(Collection<Long> ids) {
        return voyageLockMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<VoyageLockDO> getVoyageLockPage(VoyageLockPageReqVO reqVO) {
        IPage<VoyageLockDO> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());

        IPage<VoyageLockDO> result = voyageLockMapper.selectPageList(page, reqVO);
        return new PageResult<VoyageLockDO>(result.getRecords(), result.getTotal());
    }

    @Override
    public List<VoyageLockRespVO> list(VoyageLockReqVO reqVO, ExImTypeEnum exImType) {

        Long allCountSo = soMapper.selectCount(
                new LambdaQueryWrapper<SoDO>()
                        .eq(SoDO::getBookingVessel, reqVO.getVesselCode())
                        .eq(SoDO::getBookingVoyage, reqVO.getVoyage())
                        .eq(SoDO::getPolCode, reqVO.getPortCode())
                        .eq(SoDO::getSoStatus, BookingConfirmed.getValue())
        );
        if (allCountSo == 0) {
            return new ArrayList<>();
        }

        Long allCountBl = blnoMapper.selectCount(
                new LambdaQueryWrapper<BlnoDO>()
                        .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
                        .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
                        .eq(BlnoDO::getPolCode, reqVO.getPortCode())
                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                        .in(BlnoDO::getSiStatus, SiStatusEnum.getValidStatus())
        );
        Long allCountBlLock = blnoMapper.selectCount(
                new LambdaQueryWrapper<BlnoDO>()
                        .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
                        .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
                        .eq(BlnoDO::getPolCode, reqVO.getPortCode())
                        .eq(BlnoDO::getBlLockFlag, true)
                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                        .in(BlnoDO::getSiStatus, SiStatusEnum.getValidStatus())
        );
        Long allCountChargeLock = blnoMapper.selectCount(
                new LambdaQueryWrapper<BlnoDO>()
                        .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
                        .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
                        .eq(BlnoDO::getPolCode, reqVO.getPortCode())
                        .eq(exImType.isEx(), BlnoDO::getExPaymentLock, true)
                        .eq(exImType.isIm(), BlnoDO::getImPaymentLock, true)
                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                        .in(BlnoDO::getSiStatus, SiStatusEnum.getValidStatus())
        );

        VoyageLockDO voyageLockDO = voyageLockMapper.selectOne(
                new LambdaQueryWrapper<>(VoyageLockDO.class)
                        .eq(VoyageLockDO::getVesselCode, reqVO.getVesselCode())
                        .eq(VoyageLockDO::getVoyage, reqVO.getVoyage())
                        .eq(VoyageLockDO::getPortCode, reqVO.getPortCode())
                        .eq(VoyageLockDO::getExImType, exImType.getValue())
                        .last("limit 1")
        );

        VoyageLockRespVO voyageLockRespVO = new VoyageLockRespVO();
        voyageLockRespVO.setVoyage(reqVO.getVoyage());
        voyageLockRespVO.setVesselCode(reqVO.getVesselCode());
        voyageLockRespVO.setPortCode(reqVO.getPortCode());
        if (voyageLockDO == null) {
            voyageLockRespVO.setVoyageLockFlag(false);
        } else {
            voyageLockRespVO.setVoyageLockFlag(voyageLockDO.getVoyageLockFlag());
            voyageLockRespVO.setUpdater(voyageLockDO.getUpdater());

            VoyageLockRespVO convert = VoyageLockConvert.INSTANCE.convert(voyageLockDO);
            voyageLockRespVO.setUpdateTime(convert.getUpdateTime());
        }

        //根据航次获取航线代码
        BlnoVslvoyDO blnoVslvoyDO = blnoVslvoyMapper.selectOne(
                new LambdaQueryWrapper<BlnoVslvoyDO>()
                        .eq(BlnoVslvoyDO::getVesselCode, reqVO.getVesselCode())
                        .eq(exImType.isEx(), BlnoVslvoyDO::getVoyageEx, reqVO.getVoyage())
                        .last("limit 1")
        );
        if (blnoVslvoyDO != null) {
            if (exImType.isEx()) {
                voyageLockRespVO.setLaneCode(blnoVslvoyDO.getLaneCodeEx());
            }
            if (exImType.isIm()) {
                voyageLockRespVO.setLaneCode(blnoVslvoyDO.getLaneCodeIm());
            }
        }

        voyageLockRespVO.setExImType(exImType.getValue());

        voyageLockRespVO.setBlCount(allCountBl);
        voyageLockRespVO.setLockSi(allCountBlLock + "/" + allCountBl);
        voyageLockRespVO.setSoCount(allCountSo);
        voyageLockRespVO.setLockCharge(allCountChargeLock + "/" + allCountBl);

        return new ArrayList<>(Collections.singletonList(voyageLockRespVO));
    }

    @Override
    public PageResult<VoyageLockImBaseVO> getBlnoLockIm(VoyageLockImPageReqVO reqVO) {

        IPage<BlnoPageRespVO> page = Page.of(reqVO.getPageNo(), reqVO.getPageSize());
        IPage<VoyageLockImBaseVO> pageResult = blnoMapper.getBlnoLockIm(page, reqVO);
        List<VoyageLockImBaseVO> voList = pageResult.getRecords();
        for (VoyageLockImBaseVO voyageLockImBaseVO : voList) {
            List<String> split = Arrays.asList(voyageLockImBaseVO.getBlnoIds().split(","));
            int blCount = split.size();
            Long doLockCount = blnoMapper.selectCount(new LambdaQueryWrapper<BlnoDO>()
                    .in(BlnoDO::getId, split)
                    .eq(BlnoDO::getDoLock, true)
            );
            Long lockChargeCount = blnoMapper.selectCount(new LambdaQueryWrapper<BlnoDO>()
                    .in(BlnoDO::getId, split)
                    .eq(BlnoDO::getImPaymentLock, true)
            );
            Set<Long> sendArriveNoticeCount = operateLogMapper.selectList(new LambdaQueryWrapper<OperateLogDO>()
                    .in(OperateLogDO::getBusinessId, split)
                    .eq(OperateLogDO::getBusinessType, BusinessTypeEnum.SI.getValue())
                    .eq(OperateLogDO::getOperateType, SiOperateTypeEnum.ARRIVAL_NOTICE.getValue())
            ).stream().map(OperateLogDO::getBusinessId).collect(Collectors.toSet());
            voyageLockImBaseVO.setLockCharge(lockChargeCount + "/" + blCount);
            voyageLockImBaseVO.setLockDo(doLockCount+ "/" + blCount);
            voyageLockImBaseVO.setSendArriveNotice(sendArriveNoticeCount.size() + "/" + blCount);
        }
        return new PageResult<>(voList, pageResult.getTotal());
    }
    @Override
    public BlnoCheckMsgReqVO lockChargeExCheck(@Valid VoyageLockReqVO reqVo) {
        Set<String> msgList = new HashSet<>(8);
        BlnoCheckMsgReqVO blnoCheckMsgReqVO = new BlnoCheckMsgReqVO();
        List<BlnoDO> blnoDOList = blnoMapper.selectList(
                new LambdaQueryWrapper<BlnoDO>()
                        .eq(BlnoDO::getBkgVesselCode, reqVo.getVesselCode())
                        .eq(BlnoDO::getBkgVoyage, reqVo.getVoyage())
                        .eq(BlnoDO::getPolCode, reqVo.getPortCode())
                        .in(BlnoDO::getSiStatus, SiStatusEnum.getValidStatus())
        );
//        for (BlnoDO blnoDO : blnoDOList) {
//            if (blnoDO.getExPaymentLock()!=null && blnoDO.getExPaymentLock()) {
////                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHARGE_LOCK_ERROR,blnoDO.getBlNo());
//                msgList.add(blnoDO.getBlNo());
//            }
//        }
//        if(!msgList.isEmpty()){
//            blnoCheckMsgReqVO.setType("alert");
//            blnoCheckMsgReqVO.setContent(String.join(",",msgList)+"费用已锁定，无需再次锁定！");
//            return blnoCheckMsgReqVO;
//        }

        for (BlnoDO blnoDO : blnoDOList) {
            //todo 批量查询
            if (blnoLocalChargeMapper.selectList(new LambdaQueryWrapper<BlnoLocalChargeDO>()
                    .eq(BlnoLocalChargeDO::getBlnoId, blnoDO.getId())
                    .eq(BlnoLocalChargeDO::getImExType, ExImTypeEnum.EX.getValue())
            ).isEmpty()) {
                //todo 国际化
                msgList.add(blnoDO.getBlNo()+"无应收费用");
            }else
            if (blnoLocalCostExService.getLocalCostByBlnoId(blnoDO.getId(), ExImTypeEnum.EX.getValue()).isEmpty()) {
                msgList.add(blnoDO.getBlNo()+"无应付费用");
            }
        }
        String content = String.join(",", msgList);
        blnoCheckMsgReqVO.setType(StringUtils.isBlank(content)?"success":"confirm");
        blnoCheckMsgReqVO.setContent(content);
        return blnoCheckMsgReqVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lockChargeEx(VoyageLockReqVO reqVO) {
//        boolean exists = blnoMapper.exists(
//                new LambdaQueryWrapper<BlnoDO>()
//                        .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
//                        .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
//                        .eq(BlnoDO::getPolCode, reqVO.getPortCode())
//                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
//                        .eq(BlnoDO::getExPaymentLock, true)
//        );
//        if(exists){
//            Assertion.assertTrue(false)
//                    .raise0(BlnoErrorCodeConstants.SI_DO_CHARGE_LOCK_ERROR.getCode(),reqVO.getVesselCode()+"/"+reqVO.getVoyage()+" "+reqVO.getPortCode());
//        }
        List<BlnoDO> blnoDOList = blnoMapper.selectList(
                new LambdaQueryWrapper<BlnoDO>()
                        .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
                        .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
                        .eq(BlnoDO::getPolCode, reqVO.getPortCode())
                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
//                        .in(BlnoDO::getSiStatus, SiStatusEnum.getValidStatus())
                        .and(w->w
                                .isNull(BlnoDO::getExPaymentLock).or()
                                .eq(BlnoDO::getExPaymentLock, false)
                        )
        );
        if(blnoDOList.isEmpty()){
            Assertion.assertTrue(false)
                    .raise0(BlnoErrorCodeConstants.SI_DO_CHARGE_LOCK_ERROR.getCode(),reqVO.getVesselCode()+"/"+reqVO.getVoyage()+" "+reqVO.getPortCode());
        }else {
            lockChargeEx(blnoDOList);
        }
    }

    public void lockChargeEx(List<BlnoDO> blnoDOList) {
        /*
需要先检查这个船名+航次+装港下面所有的生效的 SI 记录。
1、 如果SI 是集运的调空箱提单（Space Owner=SNL  、CNTR Owner=SNL 、CNTR Container Status=E），
可以没有海运费附加费、没有 Local Charge 费用，但是必须有 Local Cost 费用才能费用锁定。
2、 如果不是集运的调空提单， 海运费附加费、 Local charge 费用 、Local Cost 都必须存在费用才能费用锁定。
锁定费用的同时需要把这些提单下面所有没有转财务的 Freight 、Local Charge、Local Cost 结转财务，仅作提示。
         */

        List<BlnoDO> blnoDoListToLock = new ArrayList<>();

        for (BlnoDO blnoDO : blnoDOList) {
//            if (!blnoLocalCostMapper.exists(new LambdaQueryWrapper<BlnoLocalCostDO>()
//                    .eq(BlnoLocalCostDO::getBlnoId, blnoDO.getId())
//            )) {
////                throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_LOCAL_COST);
//            }
//            if (CntrOwnerEnum.isSnl(blnoDO.getSpaceOwner()) && CntrOwnerEnum.isSnl(blnoDO.getCntrOwner()) && blnoDO.getCntrStatus().equals(CntrStatusEnum.E.getValue())) {
//
//            } else {
//
//                if (!blnoFreightMapper.exists(new LambdaQueryWrapper<BlnoFreightDO>()
//                        .eq(BlnoFreightDO::getBlnoId, blnoDO.getId()))) {
//                    throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_FREIGHT,blnoDO.getBlNo());
//                }
//                if (!blnoLocalChargeMapper.exists(new LambdaQueryWrapper<BlnoLocalChargeDO>()
//                        .eq(BlnoLocalChargeDO::getBlnoId, blnoDO.getId()))) {
//                    throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_LOCAL_CHARGE,blnoDO.getBlNo());
//                }
//            }

            blnoDO.setExPaymentLock(true);
            blnoDoListToLock.add(blnoDO);
        }

        if(!blnoDoListToLock.isEmpty()){
            Set<String> result = new HashSet<>();

            blnoMapper.updateBatch(blnoDoListToLock);
            saveOperateLogBatch(blnoDoListToLock, EXPORT_LOCK_FREIGHT.getValue(), OPERATE_SI_SEVENTEEN.getValue(), EXPORT_LOCK_FREIGHT.getLabel());
        }

    }
    @Override
    public BlnoCheckMsgReqVO lockChargeCheck(List<VoyageLockImReqVO> reqVos) {
        List<String> msgList = new ArrayList<>();
        List<BlnoDO> blnoDoListAll = new ArrayList<>();
        BlnoCheckMsgReqVO blnoCheckMsgReqVO = new BlnoCheckMsgReqVO();
        //使用page 冗余的id 直接使用ids 来查询
        for (VoyageLockImReqVO reqVo : reqVos) {
            List<String> split = Arrays.asList(reqVo.getBlnoIds().split(","));

            //再次验证 使用ids加速查询
            List<BlnoDO> blnoDOList = blnoMapper.selectList(
                    new LambdaQueryWrapper<BlnoDO>()
                            .in(BlnoDO::getId, split)
                            .eq(BlnoDO::getBkgVesselCode, reqVo.getVesselCode())
                            .eq(BlnoDO::getBkgVoyage, reqVo.getVoyage())
                            .eq(BlnoDO::getPolCode, reqVo.getPortCode())
                            .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                            .in(BlnoDO::getSiStatus, SiStatusEnum.getValidStatus())
            );
            blnoDoListAll.addAll(blnoDOList);

        }
        for (BlnoDO blnoDO : blnoDoListAll) {
            if (blnoDO.getImPaymentLock()!=null && blnoDO.getImPaymentLock()) {
//                throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_DO_CHARGE_LOCK_ERROR,blnoDO.getBlNo());
                msgList.add(blnoDO.getBlNo());
            }
        }
        if(!msgList.isEmpty()){
            blnoCheckMsgReqVO.setType("alert");
            blnoCheckMsgReqVO.setContent(String.join(",",msgList)+"费用已锁定，无需再次锁定！");
            return blnoCheckMsgReqVO;
        }

        for (BlnoDO blnoDO : blnoDoListAll) {
            //todo 批量查询
            if (blnoLocalChargeMapper.selectList(new LambdaQueryWrapper<BlnoLocalChargeDO>()
                    .eq(BlnoLocalChargeDO::getBlnoId, blnoDO.getId())
                    .eq(BlnoLocalChargeDO::getImExType, ExImTypeEnum.IM.getValue())
            ).isEmpty()) {
                //todo 国际化
                msgList.add(blnoDO.getBlNo()+"无应收费用");
            }else
            if (blnoLocalCostExService.getLocalCostByBlnoId(blnoDO.getId(), ExImTypeEnum.IM.getValue()).isEmpty()) {
                msgList.add(blnoDO.getBlNo()+"无应付费用");
            }
        }
        blnoCheckMsgReqVO.setType("confirm");
        blnoCheckMsgReqVO.setContent(String.join(",",msgList));
        return blnoCheckMsgReqVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lockChargeIm(List<VoyageLockImReqVO> reqVos) {

        List<BlnoDO> blnoDoListToLock = new ArrayList<>();
        //使用page 冗余的id 直接使用ids 来查询
        for (VoyageLockImReqVO reqVo : reqVos) {
            List<Long> split = Arrays.stream(reqVo.getBlnoIds().split(",")).map(Long::parseLong).collect(Collectors.toList());
            //不存在需要锁定的记录
            if (!blnoMapper.exists(new LambdaQueryWrapper<BlnoDO>()
                    .in(BlnoDO::getId, split)
                    .and(wrapper ->
                            wrapper
                                    .eq(BlnoDO::getImPaymentLock, false).or()
                                    .isNull(BlnoDO::getImPaymentLock)
                    )
            )) {
                BlnoVslvoyDO blnoVslvoyDO = blnoVslvoyMapper.selectOne(new LambdaQueryWrapper<BlnoVslvoyDO>()
                        .in(BlnoVslvoyDO::getBlnoId, split)
                        .last("limit 1")
                );
                if(blnoVslvoyDO!=null){
                    Assertion.assertTrue(false).raise0(VoyageLockErrorCodeConstants.VOYAGE_CHARGE_LOCK_EXISTS.getCode(), blnoVslvoyDO.getVesselCode()+"/"+blnoVslvoyDO.getVoyageIm());
                }
            }
            //再次验证 使用ids加速查询
            List<BlnoDO> blnoDOList = blnoMapper.selectBatchIds( split);

            for (BlnoDO blnoDO : blnoDOList) {
//                if (!blnoLocalCostMapper.exists(new LambdaQueryWrapper<BlnoLocalCostDO>()
//                        .eq(BlnoLocalCostDO::getBlnoId, blnoDO.getId())
//                )) {
//                    throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_LOCAL_COST);
//                }
//            if (!blnoFreightMapper.exists(new LambdaQueryWrapper<BlnoFreightDO>()
//                    .eq(BlnoFreightDO::getBlnoId, blnoDO.getId()))) {
//                throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_FREIGHT,blnoDO.getBlNo());
//            }
//                if (!blnoLocalChargeMapper.exists(new LambdaQueryWrapper<BlnoLocalChargeDO>()
//                        .eq(BlnoLocalChargeDO::getBlnoId, blnoDO.getId()))) {
//                    throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_LOCAL_CHARGE,blnoDO.getBlNo());
//                }


                blnoDO.setImPaymentLock(true);
                blnoDoListToLock.add(blnoDO);
            }


        }
        if(!blnoDoListToLock.isEmpty()){
            blnoMapper.updateBatch(blnoDoListToLock);
            saveOperateLogBatch(blnoDoListToLock, IMPORT_LOCK_FREIGHT.getValue(), OPERATE_SI_SEVENTEEN.getValue(), IMPORT_LOCK_FREIGHT.getLabel());
        }


    }

    @Override
    public void lockVoyage(HttpServletResponse response, VoyageLockReqVO reqVO, ExImTypeEnum exImType) {
        VoyageLockDO voyageLock1 = findVoyageLock(reqVO.getVesselCode(), reqVO.getVoyage(), reqVO.getPortCode(), exImType.getValue());
        if (voyageLock1 != null) {
            throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.VOYAGE_LOCK_EXISTS);
        }
        /*  https://snls.yuque.com/mky1i1/lyofvf/lz1gtc52gec2dg1e#mXNr2
            校验：
            1、判断这个船名+航次+装货港下面是否存在待处理的 SO 未处理。，如果有不能锁船。提示“
            2、 判断这个船名+航次+装货港下面是否存在【订舱已确认】的 SO 没有提交 SI 的，如果有不能锁船。提示“存在 SO 订舱号 xxxxxxx，没有提交 SI”。
            3、判断这个船名+航次+装货港下面是否存在 SO修改审批中记录，如果有不能锁船。提示“存在 SO 订舱号 xxxxx，修改申请记录需处理”
            4、判断这个船名+航次+装货港下面是否存在待处理的 SI。
            5、判断这个船名+航次+装货港下面是否存在待处理的 SI 修改申请审批记录。
            6、判断这个船名+航次+装货港下面是否存在待处理的 SI 拆分/合并申请记录。
            7、判断这个船名+航次+装货港下面是否存在 SI 没有出口费用锁。如果有不能锁定航次。
            8、船名+航次+港口必须有 ATU 时间才能锁船。
            操作：
            这个船名+航次+装货港下面的提单打上提单锁。
            然后再航次锁，即生成一条船名+航次+装港+锁定标识（Y）+锁定人+锁定时间的记录。
         */

        //1、判断这个船名+航次+装货港下面是否存在待处理的 SO 未处理。，如果有不能锁船。提示“
        IPage<SoDO> page1 = Page.of(1, 10);
        SoPageReqVO soPageReqVo1 = new SoPageReqVO();
        soPageReqVo1.setBookingVessel(reqVO.getVesselCode());
        soPageReqVo1.setBookingVoyage(reqVO.getVoyage());
        soPageReqVo1.setPolCode(reqVO.getPortCode());
        soPageReqVo1.setSoStatusList(new String[]{
                SoStatusEnum.BookingProcessing.getValue()
                ,SoStatusEnum.BookingPendingSpace.getValue()
                ,SoStatusEnum.AwaitingMaterialUploaded.getValue()
                ,SoStatusEnum.CustomerUploadedMaterial.getValue()
        });
        // 不管用哪个航次查都显示第一程liner的船名航次。 第一程liner 冗余在主表
        IPage<SoDO> result1 = soMapper.selectPageList(page1, soPageReqVo1);
        if (result1.getTotal()>0) {
            String bookingNos = result1.getRecords().stream().map(SoDO::getBookingNo).distinct().collect(Collectors.joining(","));
            response.setHeader("ERRORRESULTERRORRESULT", result1.getRecords().toString());
            response.setHeader("ERRORRESULTERRORRESULTSTAGE", "1、判断这个船名+航次+装货港下面是否存在待处理的 SO 未处理。，如果有不能锁船。提示");
            Assertion.assertTrue(false).raise0(SoErrorCodeConstants.SO_NEED_CONFIRM_ERROR.getCode(),
                    bookingNos);
        }
        //2、 判断这个船名+航次+装货港下面是否存在【订舱已确认】的 SO 没有提交 SI 的，如果有不能锁船。提示“存在 SO 订舱号 xxxxxxx，没有提交 SI”。
        List<SoDO> soDoList2 = soMapper.selectList(new LambdaQueryWrapper<SoDO>()
                .eq(SoDO::getBookingVessel, reqVO.getVesselCode())
                .eq(SoDO::getBookingVoyage, reqVO.getVoyage())
                .eq(SoDO::getPolCode, reqVO.getPortCode())
                .eq(SoDO::getSoStatus, SoStatusEnum.BookingConfirmed.getValue())
        );
        for (SoDO soDO : soDoList2) {

            //"没有提交 SI 的"指的是SI没有创建   或者SI创建了 状态不对  已删除
            List<BlnoDO> blnoDos = blnoMapper.selectList(
                    new LambdaQueryWrapper<BlnoDO>()
                            .eq(BlnoDO::getBlNo, soDO.getBlNo())
            );
            if(blnoDos.isEmpty()){
                Assertion.assertTrue(false).raise0(VoyageLockErrorCodeConstants.SO_NOT_EXISTS_BLNO.getCode(),
                        soDO.getBookingNo());
            }
        }

        //3、判断这个船名+航次+装货港下面是否存在 SO修改审批中记录，如果有不能锁船。提示“存在 SO 订舱号 xxxxx，修改申请记录需处理”
        IPage<SoRespVO> page3 = Page.of(1, 10);
        SoPageReqVO soPageReqVo3 = new SoPageReqVO();
        soPageReqVo3.setPolCode(reqVO.getPortCode());
        soPageReqVo3.setBookingVoyage(reqVO.getVoyage());
        soPageReqVo3.setBookingVessel(reqVO.getVesselCode());
        soPageReqVo3.setSoAmendStatusList(
                new String[]{ContentChangeStatusEnum.BOOKING_CHANGE_PROCESSING.getValue()
                        ,ContentChangeStatusEnum.BOOKING_CHANGE_AWAITING_MATERIAL_UPLOADED.getValue()
                        ,ContentChangeStatusEnum.BOOKING_CANCEL_PROCESSING.getValue()}
        );
        // 不管用哪个航次查都显示第一程liner的船名航次。 第一程liner 冗余在主表
        IPage<SoRespVO> result3 = soMapper.getSoPageAmend(page3, soPageReqVo3);

        if (result3.getTotal()>0) {
            Assertion.assertTrue(false).raise0(SO_AMEND_APPLYING_EXISTS.getCode(),
                    result3.getRecords().get(0).getBookingNo());
        }

        //4、判断这个船名+航次+装货港下面是否存在待处理的 SI。
        IPage<BlnoPageRespVO> page4 = Page.of(1, 10);
        BlnoPageReqVO blnoPageReqVO = new BlnoPageReqVO();
        blnoPageReqVO.setPolCode(reqVO.getPortCode());
        blnoPageReqVO.setBkgVesselCode(reqVO.getVesselCode());
        blnoPageReqVO.setBkgVoyage(reqVO.getVoyage());
        blnoPageReqVO.setSiStatus(new String[]{SiStatusEnum.BookingProcessing.getValue()});

        IPage<BlnoPageRespVO> pageResult4 = blnoMapper.getBlnoBaseInfo(page4, blnoPageReqVO);
        if(pageResult4.getTotal()>0){
            String blnos = pageResult4.getRecords().stream().map(BlnoPageRespVO::getBlNo).collect(Collectors.joining(","));
            Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.BLNO_BOOKING_PROCESSING.getCode(),
                    blnos);
        }
        //5、判断这个船名+航次+装货港下面是否存在待处理的 SI 修改申请审批记录。
        IPage<ConfirmNoticeRespVO> page5 = Page.of(1, 1);
        ConfirmNoticePageReqVO pageReqVO = new ConfirmNoticePageReqVO();
        pageReqVO.setCurrentPort(reqVO.getPortCode());
        pageReqVO.setVoyageIm(reqVO.getVoyage());
        pageReqVO.setVesselCode(reqVO.getVesselCode());
        pageReqVO.setStatus(new String[]{ConfirmNoticeStatusEnum.PENDING.getValue()});
        pageReqVO.setNotices(new String[]{ConfirmNoticeEnum.MCN.getValue(),ConfirmNoticeEnum.COD.getValue(),ConfirmNoticeEnum.SIC.getValue()});
        IPage<ConfirmNoticeRespVO> pageResult5 = confirmNoticeMapper.getConfirmNoticeExPage(page5, pageReqVO);
        if(pageResult5.getTotal()>0){
            response.setHeader("ERRORRESULTERRORRESULT", pageResult5.getRecords().toString());
            response.setHeader("ERRORRESULTERRORRESULTSTAGE", "5、判断这个船名+航次+装货港下面是否存在待处理的 SI 修改申请审批记录。");
            Assertion.assertTrue(false).raise0(BlnoErrorCodeConstants.BLNO_PENDING.getCode(),
                    pageResult5.getRecords().get(0).getBlNo());
        }
        //6、判断这个船名+航次+装货港下面是否存在待处理的 SI 拆分/合并申请记录。
        IPage<Long> page6 = Page.of(1, 1);
        BlnoSpiltCombinePageReqVO blnoSpiltCombinePageReqVO = new BlnoSpiltCombinePageReqVO();
        blnoSpiltCombinePageReqVO.setBkgVoyage(reqVO.getVoyage());
        blnoSpiltCombinePageReqVO.setBkgVesselCode(reqVO.getVesselCode());
        blnoSpiltCombinePageReqVO.setPolCode(reqVO.getPortCode());
        blnoSpiltCombinePageReqVO.setApproveStatusList(new String[]{SiSplitCombineApproveStatusEnum.PENDING.getValue()});
        IPage<Long> result6 = blnoSpiltCombineMapper.selectPageList(page6,blnoSpiltCombinePageReqVO);
        if(result6.getTotal()>0){
            Assertion.assertTrue(false).raise0(SoSplitCombineErrorCodeConstants.SO_COMBINE_APPLY_NEED_CHECK.getCode());
        }

        //7、判断这个船名+航次+装货港下面是否存在 SI 没有出口费用锁。如果有不能锁定航次。
        boolean existsNotPayLock = blnoMapper.exists(
                new LambdaQueryWrapper<BlnoDO>()
                        .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
                        .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
                        .eq(BlnoDO::getPolCode, reqVO.getPortCode())
                        .eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
                        .and(exImType.isEx(),wrapper ->
                                wrapper.eq( BlnoDO::getExPaymentLock, false)
                                        .or().isNull(BlnoDO::getExPaymentLock)
                        )

        );

        if (existsNotPayLock) {
            throw ServiceExceptionUtil.exception(VoyageLockErrorCodeConstants.BLNO_NOT_EXISTS_PAYMENT_LOCK);
        }
        //8、船名+航次+港口必须有 ATU 时间才能锁船。
        VesselVoyRespDTO vesselVoyRespDto = vslvoyUtil.getVesselVoyRespDTO(reqVO.getPortCode(), reqVO.getVoyage(), reqVO.getVesselCode());
        if (vesselVoyRespDto ==null || StringUtils.isBlank(vesselVoyRespDto.getAtu())) {
            throw ServiceExceptionUtil.exception(BlnoFreightErrorCodeConstants.BLNO_FREIGHT_ETU_IS_NULL);
        }

        // 更新或插入

        //装货港下面的提单打上提单锁
        blnoMapper.update(null, new LambdaUpdateWrapper<BlnoDO>()
                .eq(BlnoDO::getBkgVesselCode, reqVO.getVesselCode())
                .eq(BlnoDO::getBkgVoyage, reqVO.getVoyage())
                .eq(BlnoDO::getPolCode, reqVO.getPortCode())
                .set(BlnoDO::getBlLockFlag, true)
        );
        // 然后再航次锁，即生成一条船名+航次+装港+锁定标识（Y）+锁定人+锁定时间的记录。

        VoyageLockDO voyageLockDO = voyageLockMapper.selectOne(new LambdaQueryWrapper<VoyageLockDO>()
                .eq(VoyageLockDO::getVesselCode, reqVO.getVesselCode())
                .eq(VoyageLockDO::getVoyage, reqVO.getVoyage())
                .eq(VoyageLockDO::getPortCode, reqVO.getPortCode())
                .eq(VoyageLockDO::getExImType, exImType.getValue())
                .last("limit 1")
        );
        if(voyageLockDO==null){
            VoyageLockDO voyageLock = new VoyageLockDO();
            voyageLock.setVesselCode(reqVO.getVesselCode());
            voyageLock.setVoyage(reqVO.getVoyage());
            voyageLock.setPortCode(reqVO.getPortCode());
            voyageLock.setVoyageLockFlag(true);
            voyageLock.setExImType(exImType.getValue());

            voyageLockMapper.insert(voyageLock);
        }else {

            if(!voyageLockDO.getVoyageLockFlag()){
                voyageLockDO.setVoyageLockFlag(true);
                voyageLockMapper.updateById(voyageLockDO);
            }
        }

    }

    @Override
    public VoyageLockDO findVoyageLock(String vesselCode, String voyage, String portCode, String exImType) {
        VoyageLockDO voyageLock = voyageLockMapper.selectOne(
                new LambdaQueryWrapper<>(VoyageLockDO.class)
                        .eq(VoyageLockDO::getVesselCode, vesselCode)
                        .eq(VoyageLockDO::getVoyage, voyage)
                        .eq(VoyageLockDO::getPortCode, portCode)
                        .eq(VoyageLockDO::getExImType, exImType)
                        .last("limit 1")
        );
        return voyageLock;
    }

    private void saveOperateLogBatch(List<BlnoDO> blnoDos, String operateType, String operateTypeSub, String operateDesc) {
        List<OperateLogDO> operateLogDos = new ArrayList<>();
        blnoDos.forEach(n -> {
            OperateLogDO operateLog = new OperateLogDO();
            operateLog.setBusinessId(n.getId());
            operateLog.setBusinessNo(n.getBookingNo());
            operateLog.setBusinessType(BusinessTypeEnum.SI.getValue());
            operateLog.setOperateType(operateType);
            operateLog.setOperateTypeSub(operateTypeSub);
            operateLog.setOperateDesc(operateDesc);
            operateLogDos.add(operateLog);
        });
        operateLogMapper.insertBatch(operateLogDos);
    }
}
